// 对战房间
package internal

import (
	"BJXM_Shooting/msg"
	"crypto/md5"
	"fmt"
	"github.com/name5566/leaf/gate"
	"github.com/name5566/leaf/log"
	"math/rand"
	"sync"
	"time"
)

var (
	battleRoom = struct {
		sync.RWMutex
		data map[string]*Room
	}{data: make(map[string]*Room)}
)

type Room struct {
	Desk       string           //房间桌号
	Users      map[string]*User //房间内的成语
	ShortIndex int              //射箭顺序索引
}

func init() {
	handler(&msg.C2S_BattleReady{}, C2S_BattleReady)
	handler(&msg.C2S_BattleLoadComplete{}, C2S_BattleLoadComplete)
	handler(&msg.C2S_BattleEmoji{}, C2S_BattleEmoji)
	handler(&msg.C2S_BattleShot{}, C2S_BattleShot)
	handler(&msg.C2S_BattlePlayerLeave{}, C2S_BattlePlayerLeave)
	handler(&msg.C2S_BattleShotComplete{}, C2S_BattleShotComplete)

}

// 准备游戏(战斗结束一盘后,再来一局时用的)
// 此操作是建立在当前房间存在的情况下,当有一人离开房间,则房间消失不存在.
func C2S_BattleReady(args []interface{}) {
	msgInfo := args[0].(*msg.C2S_BattleReady)
	agentObj := args[1].(gate.Agent)

	// 验证是否登录授权 ====================================================
	user := agentObj.UserData().(*User)
	if user.data == nil || !user.auth {
		agentObj.WriteMsg(&msg.S2C_MatchingError{
			ID:  msg.S2C_ID_InnerError,
			Msg: "账户未登录",
		})
		agentObj.Close()
		return
	}
	//===================================================================

	log.Release("[玩家准备] %s 是否准备:%v", user.data.Nickname, msgInfo.OK)
	if msgInfo.OK {
		user.state = stateReady
	} else {
		user.state = stateLobby
	}

	// 检测是否可以重新开始了,就是检测玩家身上的状态是否都是stateReady
	desk := user.desk
	room := getRoom(desk)
	account := make([]string, 0)
	// 当发现有玩家未准备就返回
	for key, value := range room.Users {
		if value.state != stateReady {
			return
		}
		// 设置设置玩家为加载
		value.state = stateLoadBattle
		account = append(account, key)
	}
}

// 玩家离开房间
func C2S_BattlePlayerLeave(args []interface{}) {

}

// 加载战斗完毕
func C2S_BattleLoadComplete(args []interface{}) {
	msgInfo := args[0].(*msg.C2S_BattleLoadComplete)
	agentObj := args[1].(gate.Agent)

	// 验证是否登录授权 ====================================================
	user := agentObj.UserData().(*User)
	if user.data == nil || !user.auth {
		agentObj.WriteMsg(&msg.S2C_MatchingError{
			ID:  msg.S2C_ID_InnerError,
			Msg: "账户未登录",
		})
		agentObj.Close()
		return
	}
	//===================================================================
	log.Release("[加载战斗完毕] %s 是否完毕:%v", user.data.Nickname, msgInfo.OK)
	if msgInfo.OK {
		user.state = stateGame
	} else {
		user.state = stateLobby
	}

	// 判断是否全部加载完毕,然后发送射箭,让客户端可以射箭
	room := getRoom(user.desk)
	for _, v := range room.Users {
		if v.state != stateGame {
			return
		}
	}
	// 告知下一位射箭
	battleNextShot(room.Desk)

}

// 发出射箭
func C2S_BattleShot(args []interface{}) {
	msgInfo := args[0].(*msg.C2S_BattleShot)
	agentObj := args[1].(gate.Agent)

	// 验证是否登录授权 ====================================================
	user := agentObj.UserData().(*User)
	if user.data == nil || !user.auth {
		agentObj.WriteMsg(&msg.S2C_MatchingError{
			ID:  msg.S2C_ID_InnerError,
			Msg: "账户未登录",
		})
		agentObj.Close()
		return
	}
	//===================================================================
	// 给射箭玩家加分
	user.score += msgInfo.Score

	// 通知其他玩家射箭情况
	room := getRoom(user.desk)
	for _, v := range room.Users {
		v.WriteMsg(&msg.S2C_BattleShot{
			PlayerIndex: user.index,
			Score:       msgInfo.Score,
			AimingPoint: msgInfo.AimingPoint,
		})
	}
}

// 战斗-箭中靶了,射箭完毕.然后服务器可以通知下一位射箭者
func C2S_BattleShotComplete(args []interface{}) {
	msgInfo := args[0].(*msg.C2S_BattleShotComplete)
	agentObj := args[1].(gate.Agent)

	// 验证是否登录授权 ====================================================
	user := agentObj.UserData().(*User)
	if user.data == nil || !user.auth {
		agentObj.WriteMsg(&msg.S2C_MatchingError{
			ID:  msg.S2C_ID_InnerError,
			Msg: "账户未登录",
		})
		agentObj.Close()
		return
	}
	//===================================================================
	// 告诉客户端下一个
	if msgInfo.OK {
		room := getRoom(user.desk)
		battleNextShot(room.Desk)
	}
}

// 发出表情
func C2S_BattleEmoji(args []interface{}) {

}

// 创建战斗房间
func createBattleRoom(player1, player2 *User) msg.S2C_Matching {
	log.Release("[创建房间]玩家:%s 玩家:%s", player1.data.Nickname, player2.data.Nickname)
	// 随机射箭顺序
	index1 := rand.Intn(2)
	index2 := 0
	if index1 == 0 {
		index2 = 1
	} else {
		index2 = 0
	}

	// 由两个人账号字符串拼接然后MD5
	str := player1.data.Account + player2.data.Account
	desk := fmt.Sprintf("%x", md5.Sum([]byte(str)))

	// 设置射箭/桌号/顺序/状态
	// 创建房间的同时,直接就让玩家加载战斗场景了
	player1.desk, player1.index, player1.state = desk, index1, stateLoadBattle
	player2.desk, player2.index, player2.state = desk, index2, stateLoadBattle

	// 创建房间到map中
	battleRoom.Lock()
	temp := make(map[string]*User)
	temp[player1.data.Account] = player1
	temp[player2.data.Account] = player2
	battleRoom.data[desk] = &Room{
		Desk:       desk,
		Users:      temp,
		ShortIndex: -1, //从-1开始
	}
	battleRoom.Unlock()
	// 生成告诉客户端匹配到的玩家
	info := msg.S2C_Matching{
		Obj: []msg.MatchingInfo{
			{
				Account:  player1.data.Account,
				Index:    index1,
				Nickname: player1.data.Nickname,
				Avatar:   player1.data.Avatar,
				Level:    player1.data.Level,
			},
			{
				Account:  player2.data.Account,
				Index:    index2,
				Nickname: player2.data.Nickname,
				Avatar:   player2.data.Avatar,
				Level:    player2.data.Level,
			},
		},
	}
	// 针对射箭索引号进行排序返回,这里只有两个玩家. 所以只需要很简单的交换就可以了
	if info.Obj[0].Index == 1 {
		info.Obj[0], info.Obj[1] = info.Obj[1], info.Obj[0]
	}
	// 检测加载到游戏情况
	detectionLoadGameState(desk)

	return info
}

// 检测加载到游戏情况
// 设定一个超时的时间(秒),当超过指定时间时,就让这场游戏终止.
func detectionLoadGameState(desk string) {
	room := getRoom(desk)
	if room == nil {
		log.Error("获取的房间不存在:%s", desk)
		return
	}
	// 检查对方如果n秒内如果还没到stateGame状态则判定为离开(网络不适)
	skeleton.AfterFunc(20*time.Second, func() {
		for _, v := range room.Users {
			if v.state != stateGame {
				log.Release("[%s]超过20秒未载入游戏,终止本场游戏", v.data.Nickname)
				// 终止本场游戏,不损耗分数
				battleExceptionEnd(room, msg.S2C_ID_BattleExceptionNotLoaded, "有玩家未载入游戏,游戏结束")
				break
			}
		}
	})
}

// 游戏终止
func battleEnd(room *Room, writeMsg *msg.S2C_BattleEnd) {
	if room == nil || room.Users == nil || len(room.Users) == 0 {
		return
	}
	for _, v := range room.Users {
		if v != nil {
			// 通知客户端
			v.WriteMsg(writeMsg)
			// 调整身上的分数
			v.data.Gold += writeMsg.FinalIncome
		}
	}
}

// 游戏异常结束
func battleExceptionEnd(room *Room, id int, message string) {
	if room == nil || room.Users == nil || len(room.Users) == 0 {
		return
	}
	for _, v := range room.Users {
		if v != nil {
			v.WriteMsg(&msg.S2C_BattleException{
				ID:  id,
				Msg: message,
			})
		}
	}
}

// 获取一个房间
func getRoom(desk string) *Room {
	battleRoom.RLock()
	room := battleRoom.data[desk]
	battleRoom.RUnlock()
	return room
}

// 让下一位射箭
func battleNextShot(desk string) {
	room := getRoom(desk)
	room.ShortIndex++
	if room.ShortIndex == 2 {
		room.ShortIndex = 0
	}

	// 找到指定账号
	specific := new(User)
	for i, v := range room.Users {
		if v.index == room.ShortIndex {
			specific = room.Users[i]
			break
		}
	}

	if specific == nil {
		log.Error("获取下一位射箭者失败:%s", desk)
		return
	}
	// 发送下一位射箭
	for _, v := range room.Users {
		v.WriteMsg(&msg.S2C_BattleNextShot{
			Account: specific.data.Account,
			Index:   specific.index,
		})
	}
}
